package com.voice.core;

import com.voice.core.connection.Connection;
import com.voice.core.connection.callback.AbstractStreamListener;
import com.voice.core.connection.entity.Stream;
import com.voice.core.connection.entity.StreamData;
import com.voice.core.entity.HttpHeader;

import java.io.IOException;
import java.util.concurrent.CompletableFuture;

public class DefaultClient {

    private boolean preSend(HttpHeader headers, byte[] data){
        boolean endOfStream = false;
        int contentLength = 0;
        if (data != null && data.length != 0) {
            contentLength = data.length;
        } else {
            endOfStream = true;
        }
        headers.put("content-length", String.valueOf(contentLength));
        return endOfStream;
    }

    private AbstractStreamListener getAbstractStreamListener(CompletableFuture completableFuture){
        return new AbstractStreamListener() {
            @Override
            public void onDataRead(Connection connection, Stream stream, StreamData streamData) {
                completableFuture.complete(streamData);
            }
            @Override
            public void onStreamError(Connection connection, Stream stream, IOException exception) {
                completableFuture.completeExceptionally(exception);
            }
        };
    }

    public CompletableFuture<StreamData> sendRequest(Connection connection, HttpHeader header, byte[] data, Runnable callback) {
        final CompletableFuture<StreamData> completableFuture = new CompletableFuture();
        boolean endOfStream=preSend(header,data);

        if (!endOfStream) {
            CompletableFuture<Integer> writeCompletableFuture = connection.writeHeaders(header.toString().getBytes(), endOfStream, getAbstractStreamListener(completableFuture)).whenComplete((c, throwable) -> {
                if (throwable != null) completableFuture.completeExceptionally(throwable);
            });
            writeCompletableFuture.thenAccept((streamId) -> {
                connection.writeData(streamId,data, true,callback).whenComplete((o, t) -> {
                    if (t != null) completableFuture.completeExceptionally(t);
                });
            });
        }else{
            connection.writeHeaders(header.toString().getBytes(), endOfStream, getAbstractStreamListener(completableFuture),callback).whenComplete((c, throwable) -> {
                if (throwable != null) completableFuture.completeExceptionally(throwable);
            });
        }
        return completableFuture;
    }

    public CompletableFuture<StreamData> sendRequest(Connection connection, StreamData streamData) {
        return sendRequest(connection,streamData.getHeaders(),streamData.readAllData(),null);
    }

    public CompletableFuture<StreamData> sendRequest(Connection connection, HttpHeader header, byte[] data) {
        return sendRequest(connection,header,data,null);
    }

    public void sendResponse(Connection connection, int streamId, StreamData streamData){
        sendResponse(connection,streamId,streamData.getHeaders(),streamData.readAllData(),null);
    }

    public void sendResponse(Connection connection, int streamId, HttpHeader header, byte[] data){
        sendResponse(connection,streamId,header,data,null);
    }

    public void sendResponse(Connection connection, int streamId, StreamData streamData, Runnable callback){
        sendResponse(connection,streamId,streamData.getHeaders(),streamData.readAllData(),callback);
    }

    public void sendResponse(Connection connection, int streamId, HttpHeader header, byte[] data, Runnable callback){
        boolean endOfStream=preSend(header,data);
        if(!endOfStream){
            CompletableFuture<Integer> writeCompletableFuture=connection.writeHeaders(streamId,header.toString().getBytes(),endOfStream,null);
            writeCompletableFuture.thenAccept(id->connection.writeData(id,data,true,callback));
        }else{
            connection.writeHeaders(streamId,header.toString().getBytes(),endOfStream,null,callback);
        }
    }
}
